package lz.controls.containers
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	
	import lz.controls.core.InvalidationType;
	import lz.controls.core.UIComponent;
	import lz.controls.events.ComponentEvent;

	/**
	 *UILoader 类可让您设置要加载的内容，然后在运行时监视加载操作。 该类同时还处理已加载内容的大小调整。
	 * 如果从不同的域（沙箱）加载内容，可能由于安全原因导致内容属性不可访问。 有关详细信息，请参阅 ProLoader 类。
	 * 使用 ActionScript 设置 UILoader 类的属性将会覆盖在“属性”检查器或“组件”检查器中设置的同名参数。
	 * 该组件包装 fl.display.ProLoader。ProLoader 类处理所有的实际加载；UILoader 只提供 ProLoader 对象的可视显示。
	 * 注意：正在从不同的域或沙箱加载内容时，内容的属性可能会由于安全原因而无法访问。
	 * 有关域安全如何影响加载过程的详细信息，请参阅“ProLoader 类”。
	 */
	public class UILoader extends UIComponent
	{

		/**
		 *指示是否要将图像自动缩放到 UILoader 实例的大小。
		 */
		protected var _scaleContent:Boolean=true;

		/**
		 * 指示 UILoader 实例是否自动加载指定的内容。
		 */
		protected var _autoLoad:Boolean=true;

		/**
		 * @private (protected)
		 */
		protected var contentInited:Boolean=false;

		/**
		 * @private (protected)
		 */
		protected var _source:Object; // Can be string, instance, class, etc.

		/**
		 * @private (protected)
		 */
		protected var loader:Loader;

		/**
		 * @private (protected)
		 */
		protected var _maintainAspectRatio:Boolean=true;

		/**
		 * @private
		 */
		protected var contentClip:Sprite;
		/**
		 *默认样式
		 */
		private static var defaultStyles:Object={};

		/**
		 *  检索当前组件的默认样式映射
		 */
		public static function getStyleDefinition():Object
		{
			return defaultStyles;
		}


		//--------------------------------------
		//  Constructor
		//--------------------------------------

		/**
		 * 创建新的 UILoader 组件实例。
		 */
		public function UILoader()
		{
			super();
		}

		/**
		 * 设置大小
		 */
		override public function setSize(w:Number, h:Number):void
		{
			if (!_scaleContent && _width > 0)
			{
				return;
			}
			super.setSize(w, h);
		}

		/**
		 *获取或设置一个值，该值指示 UILoader 实例是否自动加载指定的内容。 true 值指示 UILoader 自动加载内容；false 值指示直到调用 load() 方法时才加载内容。
		 * 默认值为 true。
		 */
		public function get autoLoad():Boolean
		{
			return _autoLoad;
		}

		public function set autoLoad(value:Boolean):void
		{
			_autoLoad=value;
			if (_autoLoad && loader == null && _source != null && _source != "")
			{
				load();
			}
		}
		/**
		 *获取或设置一个值，该值指示是否要将图像自动缩放到 UILoader 实例的大小。 
		 * true 值指示将图像自动缩放到 UILoader 实例的大小；
		 * false 值指示将加载的内容自动缩放到其默认大小。
		 * 默认值为 true。
		 */
		public function get scaleContent():Boolean
		{
			return _scaleContent;
		}
		public function set scaleContent(value:Boolean):void
		{
			if (_scaleContent == value)
			{
				return;
			}
			_scaleContent=value;
			invalidate(InvalidationType.SIZE);
		}
		/**
		 *获取或设置一个值，该值指示是要保持原始图像中使用的高宽比，还是要将图像的大小调整为 UILoader 组件的当前宽度和高度。 
		 * true 值指示要保持原始高宽比；
		 * false 值指示应该将已加载内容的大小调整到 UILoader 的当前尺寸。
		 * 若要使用该属性，必须将 scaleContent 属性设置为 false，否则该属性会被忽略。
		 * 默认值为 true。
		 */
		public function get maintainAspectRatio():Boolean
		{
			return _maintainAspectRatio;
		}
		public function set maintainAspectRatio(value:Boolean):void
		{
			_maintainAspectRatio=value;
			invalidate(InvalidationType.SIZE);
		}

		/**
		 * 获取已加载的内容的字节数。
		 */
		public function get bytesLoaded():uint
		{
			return (loader == null || loader.contentLoaderInfo == null) ? 0 : loader.contentLoaderInfo.bytesLoaded;
		}

		/**
		 *  获取要加载的内容的字节数。
		 */
		public function get bytesTotal():uint
		{
			return (loader == null || loader.contentLoaderInfo == null) ? 0 : loader.contentLoaderInfo.bytesTotal;
		}

		/**
		 * 加载 ByteArray 对象中存储的二进制数据。
		 * 参数
		 * bytes:ByteArray — 一个 ByteArray 对象，该对象包含一个采用 ProLoader 类支持的格式（SWF、GIF、JPEG 或 PNG）之一的文件。
		 * context:LoaderContext (default = null) — 只应用 LoaderContext 对象的 applicationDomain 属性；不应用 LoaderContext 对象的 checkPolicyFile 和 securityDomain 属性。
		 * 引发
		 * ArgumentError — ByteArray 对象的 length 属性为 0。
		 * IllegalOperationError — context 参数的 checkPolicyFile 或 securityDomain 属性为非空。
		 * SecurityError — 提供的 context 属性的 applicationDomain 属性来自不允许的域。
		 */
		public function loadBytes(bytes:ByteArray, context:LoaderContext=null):void
		{
			_unload();
			initLoader();
			try
			{
				loader.loadBytes(bytes, context);
			}
			catch (error:*)
			{
				throw error;
			}
		}
		/**
		 * 包含使用 load() 方法或设置 source 属性加载的 SWF 文件或图像文件（JPEG、PNG 或 GIF 格式文件）的根显示对象。
		 */
		public function get content():DisplayObject
		{
			if (loader != null)
			{
				return loader.content;
			}
			else if (contentClip.numChildren)
			{
				return contentClip.getChildAt(0);
			}
			return null;
		}
		/**
		 * 获取或设置以下内容：绝对或相对 URL（该 URL 标识要加载的 SWF 或图像文件的位置）、库中影片剪辑的类名称、对显示对象的引用或者与组件位于同一层上的影片剪辑的实例名称。
		 * 有效的图像文件格式包括 GIF、PNG 和 JPEG。 
		 * 若要通过使用 URLRequest 对象加载资源，请使用 load() 方法。
		 * 默认值为 null。
		 */
		public function get source():Object
		{
			return _source;
		}
		public function set source(value:Object):void
		{
			if (value == "")
			{
				return;
			}
			_source=value;
			_unload();
			if (_autoLoad && _source != null)
			{
				load();
			}
		}

		/**
		 *获取一个范围在 0 到 100 之间的数字，指示已加载内容的百分比。
		 */
		public function get percentLoaded():Number
		{
			return (bytesTotal <= 0) ? 0 : bytesLoaded / bytesTotal * 100;
		}

		/**
		 * 加载指定内容，或者，如果未指定内容，则加载 source 属性标识的位置的内容。 
		 * 默认情况下，LoaderContext 对象使用当前域作为应用程序域。 若要指定不同的应用程序域、检查策略文件或更改安全域，请初始化新的 LoaderContext 对象并将其传递给该方法。
		 * 默认情况下 context 属性使用当前域。 若要指定不同的 ApplicationDomain、检查策略文件或更改 SecurityDomain，请传递新的 LoaderContext 对象。
		 * 参数
		 * request:URLRequest (default = null) — 标识要从中加载内容的位置的 URLRequest 对象。 如果未指定该值，则 source 属性的当前值将用作内容位置。
		 * context:LoaderContext (default = null) — 设置加载操作上下文的 LoaderContext 对象。
		 */
		public function load(request:URLRequest=null, context:LoaderContext=null):void
		{
			_unload();
			if ((request == null || request.url == null) && (_source == null || _source == ""))
			{
				return;
			}

			// Try and load the asset as a class/symbol/instance
			var asset:DisplayObject=getDisplayObjectInstance(source);
			if (asset != null)
			{
				contentClip.addChild(asset);
				contentInited=true;
				invalidate(InvalidationType.SIZE);
				return;
			}

			// Asset didn't load.  Try a URL Request
			var request:URLRequest=request;
			if (request == null)
			{ // Request is null, so create it using the source.
				request=new URLRequest(_source.toString());
			}
			if (context == null)
			{
				context=new LoaderContext(false, ApplicationDomain.currentDomain);
			}

			initLoader();
			loader.load(request, context);
		}

		/**
		 *删除使用 load() 方法或 source 属性加载的此 UILoader 对象的子项。
		 */
		public function unload():void
		{
			_source=null;
			_unload(true);
		}

		/**
		 * 取消当前正在对 ProLoader 实例执行的 load() 方法操作。
		 */
		public function close():void
		{
			try
			{
				loader.close();
			}
			catch (error:*)
			{
				throw error;
			}
		}

		//--------------------------------------
		//  Protected methods
		//--------------------------------------

		/**
		 * @private (protected)
		 */
		protected function _unload(throwError:Boolean=false):void
		{
			if (loader != null)
			{
				clearLoadEvents();
				contentClip.removeChild(loader);
				try
				{
					loader.close();
				}
				catch (e:Error)
				{
					// Don't throw close errors.
				}

				try
				{
					loader.unload();
				}
				catch (e:*)
				{
					// Do nothing on internally generated close or unload errors.	
					if (throwError)
					{
						throw e;
					}
				}
				loader=null;
				return;
			}

			contentInited=false;
			if (contentClip.numChildren)
			{
				contentClip.removeChildAt(0);
			}
		}

		/**
		 * @private (protected)
		 */
		protected function initLoader():void
		{
			loader=new Loader();
			loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, handleError, false, 0, true);
			loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleError, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.OPEN, passEvent, false, 0, true);
			loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, passEvent, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.INIT, handleInit, false, 0, true);
			loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, passEvent, false, 0, true);
			contentClip.addChild(loader);
		}

		/**
		 * @private (protected)
		 */
		protected function handleComplete(event:Event):void
		{
			clearLoadEvents();
			passEvent(event);
		}

		/**
		 * @private (protected)
		 */
		protected function passEvent(event:Event):void
		{
			dispatchEvent(event);
		}

		/**
		 * @private (protected)
		 */
		protected function handleError(event:Event):void
		{
			passEvent(event);
			clearLoadEvents();
			loader.contentLoaderInfo.removeEventListener(Event.INIT, handleInit);
		}

		/**
		 * @private (protected)
		 */
		protected function handleInit(event:Event):void
		{
			loader.contentLoaderInfo.removeEventListener(Event.INIT, handleInit);
			contentInited=true;
			passEvent(event);
			invalidate(InvalidationType.SIZE);
		}

		/**
		 * @private (protected)
		 */
		protected function clearLoadEvents():void
		{
			// clears all events except for INIT:
			loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, handleError);
			loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleError);
			loader.contentLoaderInfo.removeEventListener(Event.OPEN, passEvent);
			loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, passEvent);
			loader.contentLoaderInfo.removeEventListener(HTTPStatusEvent.HTTP_STATUS, passEvent);
			loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, handleComplete);
		}

		/**
		 * @private (protected)
		 */
		override protected function draw():void
		{
			if (isInvalid(InvalidationType.SIZE))
			{
				drawLayout();
			}
			super.draw();
		}

		/**
		 * @private (protected)
		 */
		protected function drawLayout():void
		{
			if (!contentInited)
			{
				return;
			}
			var resized:Boolean=false;

			var w:Number;
			var h:Number;
			if (loader)
			{
				var cl:LoaderInfo=loader.contentLoaderInfo;
				w=cl.width;
				h=cl.height
			}
			else
			{
				w=contentClip.width;
				h=contentClip.height;
			}

			var newW:Number=_width;
			var newH:Number=_height;
			if (!_scaleContent)
			{
				_width=contentClip.width;
				_height=contentClip.height;
			}
			else
			{
				sizeContent(contentClip, w, h, _width, _height);
			}

			if (newW != _width || newH != _height)
			{
				dispatchEvent(new ComponentEvent(ComponentEvent.RESIZE, true));
			}
		}

		/**
		 * @private (protected)
		 */
		protected function sizeContent(target:DisplayObject, contentWidth:Number, contentHeight:Number, targetWidth:Number, targetHeight:Number):void
		{
			var w:Number=targetWidth;
			var h:Number=targetHeight;
			if (_maintainAspectRatio)
			{ // Find best fit. Center vertically or horizontally
				var containerRatio:Number=targetWidth / targetHeight;
				var imageRatio:Number=contentWidth / contentHeight;

				if (containerRatio < imageRatio)
				{
					h=w / imageRatio;
				}
				else
				{
					w=h * imageRatio;
				}
			}
			target.width=w;
			target.height=h;
			target.x=targetWidth / 2 - w / 2;
			target.y=targetHeight / 2 - h / 2;
		}

		/**
		 * @private (protected)
		 */
		override protected function configUI():void
		{
			super.configUI();
			contentClip=new Sprite();
			addChild(contentClip);
		}
	}
}
